package util.http.client4;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import util.file.FileOperation;


public class HttpClientPoolUtil {

	private static final Logger logger = LoggerFactory.getLogger(HttpClientPoolUtil.class);
	
	public static final String UTF8 = "UTF-8";
	public static final String GBK = "GBK";
	public static final String GB2312 = "GB2312";
	
	public static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";
	public static final String CONTENT_TYPE_JSON = "application/json";
	
	public static final int SocketTimeout = 8 * 1000;
	public static final int ConnectTimeout = 8 * 1000;
	public static final int ConnectionRequestTimeout = 8 * 1000;
	
	public static final int MaxTotal = 500;
	public static final int DefaultMaxPerRoute = 50;
	public static final String  DEFAULT_UPLOAD_KEY = "file";
	
	public static RequestConfig requestConfig = null;
	public static PoolingHttpClientConnectionManager connectionManager = null;
	
	static {
		getRequestConfig();
		getConnectionManager();
	}
	
	
	private static void getRequestConfig() {
		if (requestConfig == null) {
			RequestConfig Default = RequestConfig.DEFAULT;
			requestConfig = RequestConfig.copy(Default).setSocketTimeout(SocketTimeout).setConnectTimeout(ConnectTimeout)
					.setConnectionRequestTimeout(ConnectionRequestTimeout).setRedirectsEnabled(true).setCircularRedirectsAllowed(false).build();
		}
	}
	
	private static void getConnectionManager() {
		if (connectionManager != null) {
			connectionManager = new PoolingHttpClientConnectionManager();
			connectionManager.setMaxTotal(MaxTotal);
			connectionManager.setDefaultMaxPerRoute(DefaultMaxPerRoute);
		}
	}
	
	public static CloseableHttpClient getCloseableHttpClient(RequestConfig config) {
		HttpClientBuilder httpClientBuilder = HttpClients.custom().setConnectionManager(connectionManager);
		if (config == null) {
			httpClientBuilder.setDefaultRequestConfig(requestConfig);
		} else {
			httpClientBuilder.setDefaultRequestConfig(config);
		}
		return httpClientBuilder.build();
	}
	
	
	public static HttpClientResponse post(String url, Map<String, String> param, Map<String, String> headers) throws ClientProtocolException, IOException {
		return post(url, param, headers, UTF8,null);
	}
	
	public static HttpClientResponse post(String url, Map<String, String> param) throws ClientProtocolException, IOException {
		return post(url, param, null, UTF8,null);
	}
	
	public static HttpClientResponse post(String url, Map<String, String> param,String charset) throws ClientProtocolException, IOException {
		return post(url, param, null, charset,null);
	}
	
	public static HttpClientResponse post(String url,Map<String, String> param,Map<String, String> headers,String charset,RequestConfig config) throws ClientProtocolException, IOException{
		logger.info("post request url : {}", url);
		logger.info("post request param : {}", param);
		logger.debug("post request headers : {}", headers);
		logger.debug("post request charset : {}", charset);
		
		CloseableHttpClient httpclient = getCloseableHttpClient(config);
		
		HttpPost httpPost = new HttpPost(url);
		
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpPost.addHeader(header.getKey(), header.getValue());
			}
		}

		List<NameValuePair> nvps = new ArrayList<NameValuePair>();
		if (param != null) {
			for (Entry<String, String> form : param.entrySet()) {
				nvps.add(new BasicNameValuePair(form.getKey(), form.getValue()));
			}
		}
		
		httpPost.setEntity(new UrlEncodedFormEntity(nvps,charset));
		
		CloseableHttpResponse response = httpclient.execute(httpPost);
		String respStr = null;
		HttpClientResponse responseBean = null;

		try {
			logger.info("post response StatusLine : {}", response.getStatusLine());
			
		    HttpEntity entity = response.getEntity();
		    respStr = EntityUtils.toString(entity,charset);
		    logger.info("post response entity : {}", respStr);
		   
		    responseBean = buildResp(response,respStr);
		   
		    EntityUtils.consume(entity);
		} finally {
		    response.close();
		    httpclient.close();
		}
		
		return responseBean;
	}
	

	public static HttpClientResponse post(String url,String param,Map<String, String> headers) throws ClientProtocolException, IOException{
		return post( url, param, headers,UTF8,null);
		
	}
	
	public static HttpClientResponse post(String url,String param,String charset) throws ClientProtocolException, IOException{
		return post(url, param, null, charset,null);
		
	}
	
	public static HttpClientResponse post(String url,String param) throws ClientProtocolException, IOException{
		return post(url, param, null, UTF8,null);
		
	}
	
	public static HttpClientResponse post(String url,String param,Map<String, String> headers,String charset,RequestConfig config) throws ClientProtocolException, IOException{
		logger.info("post request url : {}", url);
		logger.info("post request param : {}", param);
		logger.debug("post request headers : {}", headers);
		logger.debug("post request charset : {}", charset);
		
		CloseableHttpClient httpclient = getCloseableHttpClient(config);
		
		HttpPost httpPost = new HttpPost(url);
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpPost.addHeader(header.getKey(), header.getValue());
			}
		}

		httpPost.setEntity(new StringEntity(param, charset));
		
		CloseableHttpResponse response = httpclient.execute(httpPost);
		String respStr = null;
		HttpClientResponse responseBean = null;

		try {
			logger.info("post response StatusLine : {}", response.getStatusLine());
			
		    HttpEntity entity = response.getEntity();
		    respStr = EntityUtils.toString(entity,charset);
		    logger.info("post response entity : {}", respStr);
		    
		    responseBean = buildResp(response,respStr);
		    
		    EntityUtils.consume(entity);
		} finally {
		    response.close();
		    httpclient.close();
		}
		
		return responseBean;
		
	}
	
	
	public static HttpClientResponse get(String url) throws ClientProtocolException, IOException{
		return get(url,null,UTF8,null);
	}
	
	public static HttpClientResponse get(String url,Map<String, String> headers) throws ClientProtocolException, IOException{
		return get(url,headers,UTF8,null);
	}
	
	public static HttpClientResponse get(String url,String charset) throws ClientProtocolException, IOException{
		return get(url,null,charset,null);
	}
	
	public static HttpClientResponse get(String url,Map<String, String> headers,String charset,RequestConfig config) throws ClientProtocolException, IOException{
		logger.info("get request url : {}", url);
		
		CloseableHttpClient httpclient = getCloseableHttpClient(config);
		
		HttpGet httpGet = new HttpGet(url);
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpGet.addHeader(header.getKey(), header.getValue());
			}
		}
		
		CloseableHttpResponse response = httpclient.execute(httpGet);
		String respStr = null;
		HttpClientResponse responseBean = null;
		
		try {
			logger.info("get response StatusLine : {}", response.getStatusLine());
			
		    HttpEntity entity = response.getEntity();
		    respStr = EntityUtils.toString(entity,charset);
		    logger.info("get response entity : {}", respStr);
		    
		    responseBean = buildResp(response,respStr);
		    
		    EntityUtils.consume(entity);
		} finally {
		    response.close();
		    httpclient.close();
		}
		
		return responseBean;
	}
	
	
	
	public static HttpClientResponse put(String url, Map<String, String> param, Map<String, String> headers) throws ClientProtocolException, IOException {
		return put(url, param, headers, UTF8,null);
	}
	
	public static HttpClientResponse put(String url, Map<String, String> param) throws ClientProtocolException, IOException {
		return put(url, param, null, UTF8,null);
	}
	
	public static HttpClientResponse put(String url, Map<String, String> param,String charset) throws ClientProtocolException, IOException {
		return put(url, param, null, charset,null);
	}
	
	public static HttpClientResponse put(String url,Map<String, String> param,Map<String, String> headers,String charset,RequestConfig config) throws ClientProtocolException, IOException{
		logger.info("put request url : {}", url);
		logger.info("put request param : {}", param);
		logger.debug("put request headers : {}", headers);
		logger.debug("put request charset : {}", charset);
		
		CloseableHttpClient httpclient = getCloseableHttpClient(config);
		
		HttpPut httpPut = new HttpPut(url);
		
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpPut.addHeader(header.getKey(), header.getValue());
			}
		}

		List<NameValuePair> nvps = new ArrayList<NameValuePair>();
		if (param != null) {
			for (Entry<String, String> form : param.entrySet()) {
				nvps.add(new BasicNameValuePair(form.getKey(), form.getValue()));
			}
		}
		
		httpPut.setEntity(new UrlEncodedFormEntity(nvps,charset));
		
		CloseableHttpResponse response = httpclient.execute(httpPut);
		String respStr = null;
		HttpClientResponse responseBean = null;

		try {
			logger.info("put response StatusLine : {}", response.getStatusLine());
			
		    HttpEntity entity = response.getEntity();
		    respStr = EntityUtils.toString(entity,charset);
		    logger.info("put response entity : {}", respStr);
		   
		    responseBean = buildResp(response,respStr);
		   
		    EntityUtils.consume(entity);
		} finally {
		    response.close();
		    httpclient.close();
		}
		
		return responseBean;
	}
	

	public static HttpClientResponse put(String url,String param,Map<String, String> headers) throws ClientProtocolException, IOException{
		return post( url, param, headers,UTF8,null);
		
	}
	
	public static HttpClientResponse put(String url,String param,String charset) throws ClientProtocolException, IOException{
		return post(url, param, null, charset,null);
		
	}
	
	public static HttpClientResponse put(String url,String param) throws ClientProtocolException, IOException{
		return post(url, param, null, UTF8,null);
		
	}
	
	public static HttpClientResponse put(String url,String param,Map<String, String> headers,String charset,RequestConfig config) throws ClientProtocolException, IOException{
		logger.info("put request url : {}", url);
		logger.info("put request param : {}", param);
		logger.debug("put request headers : {}", headers);
		logger.debug("put request charset : {}", charset);
		
		CloseableHttpClient httpclient = getCloseableHttpClient(config);
		
		HttpPut httpPut = new HttpPut(url);
		
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpPut.addHeader(header.getKey(), header.getValue());
			}
		}

		httpPut.setEntity(new StringEntity(param, charset));
		
		CloseableHttpResponse response = httpclient.execute(httpPut);
		String respStr = null;
		HttpClientResponse responseBean = null;

		try {
			logger.info("put response StatusLine : {}", response.getStatusLine());
			
		    HttpEntity entity = response.getEntity();
		    respStr = EntityUtils.toString(entity,charset);
		    logger.info("put response entity : {}", respStr);
		    
		    responseBean = buildResp(response,respStr);
		    
		    EntityUtils.consume(entity);
		} finally {
		    response.close();
		    httpclient.close();
		}
		
		return responseBean;
		
	}
	
	public static HttpClientResponse downloadFile(String url,String pathName) throws ClientProtocolException, IOException{
		return downloadFile(url, null, null,pathName);
	}
	
	public static HttpClientResponse downloadFile(String url,Map<String, String> headers,RequestConfig config,String pathName) throws ClientProtocolException, IOException{
		logger.info("downloadFile request url : {}", url);
		logger.info("downloadFile request pathName : {}", pathName);
		logger.debug("downloadFile request headers : {}", headers);
		
		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpGet httpGet = new HttpGet(url);
		
		if (config == null) {
			getRequestConfig();
			httpGet.setConfig(requestConfig);
		}
		else {
			httpGet.setConfig(config);
		}
		
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpGet.addHeader(header.getKey(), header.getValue());
			}
		}
	
		CloseableHttpResponse response = httpclient.execute(httpGet);
		String respStr = null;
		HttpClientResponse responseBean = null;
		
		try {
			logger.info("downloadFile response StatusLine : {}", response.getStatusLine());
			
		    HttpEntity entity = response.getEntity();
		    InputStream inputStream = entity.getContent();
		    
		    responseBean = buildResp(response,respStr);
		    saveFile(inputStream,pathName);
		    
		    EntityUtils.consume(entity);
		} finally {
		    response.close();
		    httpGet.releaseConnection();
		}

		
		return responseBean;
	}
	
	public static HttpClientResponse uploadFile(String url,Map<String, String> param,String pathName) throws ClientProtocolException, IOException{
		return uploadFile(url, param, null, UTF8, null, pathName,DEFAULT_UPLOAD_KEY);
	}

	public static HttpClientResponse uploadFile(String url,Map<String, String> param,Map<String, String> headers,String charset,RequestConfig config,String pathName,String uploadKey) throws ClientProtocolException, IOException{
		logger.info("uploadFile request url : {}", url);
		logger.info("uploadFile request param : {}", param);
		logger.info("uploadFile request uploadKey : {}", uploadKey);
		logger.info("uploadFile request pathName : {}", pathName);
		logger.debug("uploadFile request headers : {}", headers);

		
		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpPost httpPost = new HttpPost(url);
		
		if (config == null) {
			getRequestConfig();
			httpPost.setConfig(requestConfig);
		}
		else {
			httpPost.setConfig(config);
		}
		
		
		if (headers != null) {
			for (Entry<String, String> header : headers.entrySet()) {
				httpPost.addHeader(header.getKey(), header.getValue());
			}
		}

		FileBody fileBody = new FileBody(new File(pathName));
		MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create().addPart(uploadKey, fileBody);
		
		for (Entry<String, String> val : param.entrySet()) {
			StringBody eachVal = new StringBody(val.getValue(), ContentType.create("text/plain", charset));
			multipartEntityBuilder.addPart(val.getKey(), eachVal);
		}

		HttpEntity reqEntity = multipartEntityBuilder.build();
		httpPost.setEntity(reqEntity);
		
		CloseableHttpResponse response = httpclient.execute(httpPost);
		String respStr = null;
		HttpClientResponse responseBean = null;
		
		try {
			logger.info("uploadFile response StatusLine : {}", response.getStatusLine());
			
		    HttpEntity entity = response.getEntity();
		    respStr = EntityUtils.toString(entity,charset);
		    logger.info("uploadFile response entity : {}", respStr);
		    
		    responseBean = buildResp(response,respStr);
		    
		    EntityUtils.consume(entity);
		} finally {
		    response.close();
			httpPost.releaseConnection();
		}
		
		return responseBean;
	}
	

	private static void saveFile(InputStream inputStream,String pathName){
		FileOperation fileOperation = new FileOperation(pathName);
		
		try {
			fileOperation.createBinaryFile(inputStream);
		} catch (IOException e) {
			e.printStackTrace();
			logger.error("pathName : ", pathName);
		}
	}
	
	
	private static HttpClientResponse buildResp(CloseableHttpResponse response,String respStr){
		HttpClientResponse responseBean = new HttpClientResponse();
		responseBean.setContent(respStr);
		responseBean.setHttpStatusCode(response.getStatusLine().getStatusCode());
		
		HashMap<String, String> respHeader = new HashMap<String, String>();
		int index = 1;
		
		
		for (Header header : response.getAllHeaders()) {
			String name = header.getName();
			String value = header.getValue();

			logger.info("header name : {} --- value : {}", name, value);

			if (name == null || "".equals(name)) {
				if (value != null && !"".equals(value)) {
					respHeader.put("header" + index, header.getValue());
				}
			} else {
				respHeader.put(header.getName(), header.getValue());
				if (name.equals("Set-Cookie")) {
					responseBean.setCookies(value);
				}
			}

		}
		
		responseBean.setHeaders(respHeader);
		
		return responseBean;
	}
}
